package com.crazymakercircle.mutithread.basic.create;

import com.crazymakercircle.util.Print;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

import static com.crazymakercircle.util.ThreadUtil.getCurThreadName;
import static com.crazymakercircle.util.ThreadUtil.sleepSeconds;

/**
 * 通过Callable接口和FutureTask类相结合创建线程
 * @author Marion
 * @date 2021/6/11 14:24
 */
public class CreateDemo3 {

    public static final int MAX_TURN = 5;
    public static final int COMPUTE_TIMES = 100000000;

    /**
     * 支持返回值的多线程
     */
    static class ReturnableTask implements Callable<Long> {
        //返回并发执行的时间
        @Override
        public Long call() throws Exception {
            long startTime = System.currentTimeMillis();
            Print.cfo(getCurThreadName() + " 线程运行开始.");
            Thread.sleep(1000);

            for (int i = 0; i < COMPUTE_TIMES; i++) {
                int j = i * 10000;
            }
            long used = System.currentTimeMillis() - startTime;
            Print.cfo(getCurThreadName() + " 线程运行结束.");
            return used;
        }
    }

    static class CallTask implements Callable<Long> {

        /**
         * Computes a result, or throws an exception if unable to do so.
         * @return computed result
         * @throws Exception if unable to compute a result
         */
        @Override
        public Long call() throws Exception {
            Print.cfo("[CallTask] callable return start");
            sleepSeconds(5);
            Print.cfo("[CallTask] callable return end");
            return 1L;
        }
    }

    public static void main(String args[]) throws InterruptedException {
        ReturnableTask task = new ReturnableTask();
        /**
         * 3. Future接口Future接口至少提供了三大功能：
         * （1）能够取消异步执行中的任务。
         * （2）判断异步任务是否执行完成。
         * （3）获取异步任务完成后的执行结果。
         * 5.使用Callable和FutureTask创建线程的具体步骤通过FutureTask类和Callable接口的联合使用可以创建能够获取异步执行结果的线程，
         * 具体步骤如下：
         * （1）创建一个Callable接口的实现类，并实现其call()方法，编写好异步执行的具体逻辑，可以有返回值。
         * （2）使用Callable实现类的实例构造一个FutureTask实例。
         * （3）使用FutureTask实例作为Thread构造器的target入参，构造新的Thread线程实例。
         * （4）调用Thread实例的start()方法启动新线程，启动新线程的run()方法并发执行。
         * 其内部的执行过程为：启动Thread实例的run()方法并发执行后，会执行FutureTask实例的run()方法，
         * 最终会并发执行Callable实现类的call()方法。
         * （5）调用FutureTask对象的get()方法阻塞性地获得并发线程的执行结果。
         */
        FutureTask<Long> futureTask = new FutureTask<Long>(task);
        Thread thread = new Thread(futureTask, "returnableThread");
        thread.start();

        Thread.sleep(500);
        Print.cfo(getCurThreadName() + " 让子弹飞一会儿.");
        Print.cfo(getCurThreadName() + " 做一点自己的事情.");
        for (int i = 0; i < COMPUTE_TIMES / 2; i++) {
            int j = i * 10000;
        }

        Print.cfo(getCurThreadName() + " 获取并发任务的执行结果.");

        /**
         * （1）futureTask的结果outcome不为空，callable.call()执行完成。
         * 在这种情况下，futureTast.get会直接取回outcome结果，返回给main线程（结果获取线程）。
         * （2）futureTask的结果outcome为空，callable.call()还没有执行完。在这种情况下，
         * main线程作为结果获取线程会被阻塞住，一直阻塞到callable.call()执行完成。当执行完后，
         * 最终结果会保存到outcome中，futureTask会唤醒main线程，去提取callable.call()执行结果。
         */
        try {
            Print.cfo(thread.getName() + "线程占用时间：" + futureTask.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        Print.cfo(getCurThreadName() + " 运行结束.");

        CallTask callTask = new CallTask();
        FutureTask<Long> task1 = new FutureTask<>(callTask);
        Thread demoThread = new Thread(task1, "demoThread");
        demoThread.start();
        try {
            Print.cfo(thread.getName() + "异步执行返回：" + task1.get());
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        Print.cfo(thread.getName() + "结束");
    }
}